在本系列文中,所有的程式碼以及測試都可以在 should-i-use-fp-ts 找到,今日的範例放在 src/day-10
並且有習題和測試可以讓大家練習。
在前幾天我們已經學習到了如何使用 Option
改善業務邏輯的操作流程,但還沒將目標的數值提取出來。
const fp = (xs: ReadonlyArray<number>) => pipe( // use [1, 2, 3] as an example
xs, // [1, 2, 3]
head, // { _tag: 'Some', value: 1 }
O.map(double), // { _tag: 'Some', value: 2 }
O.flatMap(inverse), // { _tag: 'Some', value: 0.5 }
);
現在要實作 getOrElse
函式來做下列兩件事情:
Some
的話,傳回運算結果 (以上面為例: 0.5
)None
的話,傳回我們設定好的 onNone
結果 (ex. 0
)type GetOrElse = <A>(onNone: () => A) => (x: O.Option<A>) => A;
const getOrElse: GetOrElse = onNone => x => x._tag === 'None' ? onNone() : x.value;
getOrElse
需要先輸入 onNone
函式,來產出運算結果為 None
時的預設結果,現在用 getOrElse
來改寫上述例子。
const imperativeOrElse = (xs: ReadonlyArray<number>) => { // normal example
try {
return inverseI(double(headI(xs))); // get
} catch {
return 0; // orElse
}
};
// use [1, 2, 3] as an example
const fpElse = (xs: ReadonlyArray<number>) => pipe(
xs, // [1, 2, 3]
head, // { _tag: 'Some', value: 1 }
O.map(double), // { _tag: 'Some', value: 2 }
O.flatMap(inverse), // { _tag: 'Some', value: 0.5 }
O.getOrElse(() => 0), // 0.5
);
實作完 getOrElse
之後,就可以妥善處理各種 if...else
的情況了,但如果函式在 None
時,想要輸出不同於原輸出的型別呢?
const imperativeOrElseW = (xs: ReadonlyArray<number>) => {
try {
return inverseI(double(headI(xs))); // get
} catch {
return 'no value'; // orElseW
}
};
沒錯,接下來要實作可以接受各種型別的 getOrElseW
,只要處理 onNone
函式的輸出型別即可。
type GetOrElseW = <A, B>(onNone: () => B) => (x: O.Option<A>) => A | B;
const getOrElseW: GetOrElseW = onNone => x => x._tag === 'None' ? onNone() : x.value;
// use [0, 2, 3] as an example
const fpElseW = (xs: ReadonlyArray<number>) => pipe(
xs, // [0, 2, 3]
head, // { _tag: 'None' }
O.map(double), // { _tag: 'None' }
O.flatMap(inverse), // { _tag: 'None' }
getOrElseW(() => 'no value'), // 'no value'
);
今天的主題在 should-i-use-fp-ts src/day-10
有習題和測試可以練習,大家可以嘗試自己能不能寫出自己的 getOrElse
和 getOrElseW
。